home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Presentations / Presentations ’96 / Papers ’96 / Scripting⁄Bare Bones / Scripting.p (for MacHack) < prev   
Encoding:
Text File  |  1996-01-01  |  38.5 KB  |  1,254 lines  |  [TEXT/PJMM]

  1.  
  2. {This is a unit to add support for scriptability to an application.}
  3. {Presented at MacHack '96, by Kevin Killion}
  4.  
  5. {--------------------------------------------------------------------------------------------------}
  6.  
  7. {©Copyright 1996, Stone House Systems, Inc.  Written by Kevin C. Killion.}
  8.  
  9. {This listing may NOT be reproduced or distributed by anyone for purposes of publication,}
  10. {distribution, compilation in a collection of source code examples, or any other purposes,}
  11. {except by written agreement with Stone House Systems, Inc. Our e-mail address is}
  12. {info@shsmedia.com, and our phone is (847)256-5813. }
  13.  
  14. {--------------------------------------------------------------------------------------------------}
  15.  
  16. {Portions of this listing are derived from Quill, written by Bennet Marks and Copyright © 1991 Apple Computer, Inc.}
  17. {In turn, Quill was based on TEStyleSample  (Copyright © 1989 Apple Computer, Inc.)}
  18.  
  19. {--------------------------------------------------------------------------------------------------}
  20.  
  21. {Notes and quirks:}
  22.  
  23. {This unit is excerpted directly from a "real" application written in TCL, and some vestigal remains of that app are }
  24. {still present.  In particular, reference is made to a some other units and classes: }
  25. { Some classes:}
  26. {KTHISAPPDoc: a subclass of CDocument }
  27. {KRow: corresponding to rows in a document }
  28.  
  29. {The CHATTY compiler switch sets whether we want extra info for debugging}
  30.  
  31. {--------------------------------------------------------------------------------------------------}
  32.  
  33. UNIT Scripting;
  34.  
  35. INTERFACE
  36.  
  37. CONST
  38.     errAECantHandleClass = -10010;
  39.     errAECantHandleType = -10009;
  40.     errAENotAnElement = -10008;
  41.     errAEIndexTooLarge = -10007;
  42.     errAENotModifiable = -10003;
  43.     errAEBadKeyForm = -10002;
  44.  
  45. {--------------------------------------------------------------------------------------------------}
  46.  
  47. IMPLEMENTATION
  48.  
  49. {$SETC CHATTY=FALSE}
  50.  
  51. USES
  52.     AEObjects, THISAPPIntf, KTHISAPPCommands;
  53.  
  54. CONST
  55.     errorStringID = 9001;
  56.  
  57.     eOnlyFirstOrLast = 1;                        {Only "first" or "last" may be used as keywords here}
  58.     eOnlyNameOrIndex = 2;                        {This object can only be referred to by name or index}
  59.     eOnlyNameIndexFirstOrLast = 3;                {This class can only be referenced by name, index, "first" or "last'}
  60.     eOnlyIndexFirstOrLast = 4;                    {This class can only be referenced by index, "first" or "last"}
  61.     eIndexNumberOutOfRange = 5;                    {Index number given is out of range of existing objects}
  62.     eContainerDoesNotContainRequestedClass = 10;        {Container does not contain requested class}
  63.     eContainerDoesNotHaveValidToken = 11;            {Container does not have valid token}
  64.  
  65.     eElementIsNotMemberOfSpecifiedContainer = 15;    {Element is not member of specified container}
  66.  
  67.     eBufferTooSmall = 20;                        {Cannot handle this property (internal buffer too small)}
  68.     eCannotHandleAPropertyOfThisType = 21;            {Cannot handle a property of this type}
  69.     ePropertyValueSpecifiedInIncorrectFormat = 22;    {Property value specified in incorrect format}
  70.     ePropertyValueSpecifiedWithIncorrectSize = 23;    {Property value specified with incorrect size}
  71.  
  72.     eThisClassUnderConstruction = 30;                    {This class under construction}
  73.     eThisPropertyUnderConstruction = 31;                {This property under construction}
  74.     eCannotHandlePropertiesOfThisClass = 32;            {Cannot handle properties of this class}
  75.  
  76.     cProperty = 'prop';
  77.     kAECoreSuite = 'core';
  78.     kAECountElements = 'cnte';
  79.     kAEGetData = 'getd';
  80.     kAESetData = 'setd';
  81.     keyAEObjectClass = 'kocl';
  82.     keyAEResult = '----';                {also, keyDirectObject = '----';}
  83.     keyAERequestedType = 'rtyp';
  84.     keyAEErrorObject = 'erob';
  85.     keyAEData = 'data';
  86.     genericErr = -1799;
  87.  
  88. TYPE
  89.     LongPointer = ^longint;
  90.     LongHandle = ^LongPointer;
  91.  
  92.     {Everyone gets to decide for himself/herself what a "token" consists of.}
  93.     {Here's what I chose, for this application}
  94.     MyTokenType = RECORD
  95.             myTokenCode: DescType;    {a code used only by app internally}
  96.             theObject: CObject;
  97.             subReference: longint;    {if an element of an object is not a "real" object, this is useful}
  98.             isAProperty: Boolean;    {if FALSE, this is an object itself; if TRUE, it refers to a property of that object}
  99.             propertyCode: DescType;    {A code defined in aete.  Field is only used if isAProperty=TRUE}
  100.         END;
  101.  
  102. CONST
  103.     myTokenSize = SIZEOF(MyTokenType);
  104.  
  105.         {codes used in myTokenCode}
  106.     winTokenCode = '*win';
  107.     docTokenCode = '*doc';
  108.     rowTokenCode = '*row';
  109.  
  110. VAR
  111.     gErrorDesc: AEDesc;
  112.     gNullDesc: AEDesc;
  113.     gInHandler, gTempBool: Boolean;
  114.     gReturnedKeywd: AEKeyWord;
  115.     gReturnedType: DescType;
  116.     gActSize: Size;
  117.  
  118.     aTokenBody: MyTokenType;
  119.  
  120. FUNCTION CheckErr (errResult, where: integer): Boolean;
  121.     VAR
  122.         item: integer;
  123.     BEGIN
  124.         IF errResult <> noErr THEN
  125.             BEGIN
  126.                 ParamText('CheckErr:  err ', N2S(errResult), ' has occurred at ', N2S(where));
  127.                 item := NoteAlert(7500, NIL);
  128.                 {error "errResult" has occured at "where" in the program}
  129.             END;
  130.  
  131.         CheckErr := (errResult <> noErr);
  132.     END;
  133.  
  134.  
  135. FUNCTION CatchErr (errResult, where: integer; VAR errToBeReturned: integer): Boolean;
  136.     BEGIN
  137.         errToBeReturned := errResult;
  138.         CatchErr := CheckErr(errResult, where);
  139.     END;
  140.  
  141.  
  142. FUNCTION QuietCatchErr (theErr: OSErr; VAR holdErr: OSErr): BOOLEAN;
  143. { this routine returns TRUE if theErr is a real error (not}
  144. {  noErr), FALSE if noErr.  In either case it stuffs theErr}
  145. {  into the VAR parameter holdErr for later use, which can}
  146. {  be particularly handy if the first parameter is an error-}
  147. {  generating function (like all the AE calls).  Unlike}
  148. {  CatchErr, QuietCatchErr does not put up an error alert.}
  149. {  INPUTS:    theErr        potential error to be checked}
  150. {              holdERR        result VAR to save the error code in}
  151.     BEGIN
  152.         holdErr := theErr;
  153.         QuietCatchErr := (theErr <> noErr);
  154.     END;
  155.  
  156.  
  157. PROCEDURE DisplayParameterInfo (ae: AppleEvent; desiredType: AEKeyword);
  158.     VAR
  159.         actualType: DescType;
  160.         actualSize: Size;
  161.         debugString: str255;
  162.         err, item: integer;
  163.     BEGIN
  164.         err := AESizeOfParam(ae, desiredType, actualType, actualSize);
  165.         debugString := STRINGOF('How was the data supplied?  Type=“', actualType, '”, size=“', actualSize : 1, '”, error=', err : 1);
  166.         ParamText(debugString, '', '', '');
  167.         item := NoteAlert(7500, NIL);
  168.     END;
  169.  
  170. {------------------------------------------------------------------------------------------------}
  171.  
  172. PROCEDURE PreHandler;        { called at the start of every AppleEvent handler}
  173.     BEGIN
  174.         gInHandler := TRUE;
  175.         gErrorDesc := gNullDesc;
  176.     END;
  177.  
  178.  
  179. PROCEDURE PostHandler (reply: AppleEvent; errNum: OSErr);
  180. {    reply        the reply AppleEvent in which the handler}
  181. {                          should return any error parameters; may be}
  182. {                        typeNull if the sender didn't ask for a reply}
  183. {    myErr        the error code generated by the handler (may be noErr)}
  184.     VAR
  185.         errDescExists: boolean;
  186.         sss: str255;
  187.         e: integer;
  188.     BEGIN
  189.         gInHandler := FALSE;
  190.  
  191.         errDescExists := (gErrorDesc.descriptorType <> typeNull);
  192.  
  193.         IF (reply.descriptorType <> typeNull) & (errNum <> noErr) & errDescExists THEN
  194.               { they want a reply; there was an error; there's an object in gErrorDesc - so send it back with the reply    }
  195.             gTempBool := CheckErr(AEPutParamDesc(reply, keyAEErrorObject, gErrorDesc), 21313);
  196.  
  197.         { in any case, if there is a gErrorDesc, now's a good time to get rid of it }
  198.         IF errDescExists THEN
  199.             BEGIN
  200.                 gTempBool := CheckErr(AEDisposeDesc(gErrorDesc), 21314);
  201.                 gErrorDesc := gNullDesc;    { just for neatness }
  202.             END;
  203.  
  204.         IF errNum <> noErr THEN
  205.             IF reply.dataHandle <> NIL THEN
  206.                 IF (errNum > 0) & (errNum <= CountIndStr(errorStringID)) THEN
  207.                     BEGIN
  208.                         GetIndString(sss, errorStringID, errNum);
  209.                         IF sss <> '' THEN
  210.                             e := AEPutParamPtr(reply, keyErrorString, typeChar, @sss[1], LENGTH(sss));
  211.                     END;
  212.  
  213.     END;    { PostHandler }
  214.  
  215.  
  216. { set a bunch of descriptors to the null descriptor}
  217. PROCEDURE InitSomeDescs (desc1Ptr, desc2Ptr, desc3Ptr, desc4Ptr, desc5Ptr: DescPtr);
  218.     BEGIN
  219.         IF desc1Ptr <> NIL THEN
  220.             desc1Ptr^ := gNullDesc;
  221.         IF desc2Ptr <> NIL THEN
  222.             desc2Ptr^ := gNullDesc;
  223.         IF desc3Ptr <> NIL THEN
  224.             desc3Ptr^ := gNullDesc;
  225.         IF desc4Ptr <> NIL THEN
  226.             desc4Ptr^ := gNullDesc;
  227.         IF desc5Ptr <> NIL THEN
  228.             desc5Ptr^ := gNullDesc;
  229.     END;
  230.  
  231.  
  232. FUNCTION DisposeSomeDescs (desc1Ptr, desc2Ptr, desc3Ptr, desc4Ptr, desc5Ptr: DescPtr): OSErr;
  233.     LABEL
  234.         9;
  235.     VAR
  236.         myErr: OSErr;
  237.         tempErr: OSErr;
  238.     BEGIN
  239.         myErr := noErr;
  240.         IF desc1Ptr = NIL THEN
  241.             GOTO 9;    { finish up }
  242.         myErr := AEDisposeDesc(desc1Ptr^);
  243.  
  244.         IF desc2Ptr = NIL THEN
  245.             GOTO 9;
  246.         tempErr := AEDisposeDesc(desc2Ptr^);
  247.         IF myErr = noErr THEN
  248.             myErr := tempErr;    { we want to keep the first real error }
  249.  
  250.         IF desc3Ptr = NIL THEN
  251.             GOTO 9;
  252.         tempErr := AEDisposeDesc(desc3Ptr^);
  253.         IF myErr = noErr THEN
  254.             myErr := tempErr;
  255.  
  256.         IF desc4Ptr = NIL THEN
  257.             GOTO 9;
  258.         tempErr := AEDisposeDesc(desc4Ptr^);
  259.         IF myErr = noErr THEN
  260.             myErr := tempErr;
  261.  
  262.         IF desc5Ptr = NIL THEN
  263.             GOTO 9;
  264.         tempErr := AEDisposeDesc(desc5Ptr^);
  265.         IF myErr = noErr THEN
  266.             myErr := tempErr;
  267.  
  268. 9:    { finish up }
  269.         DisposeSomeDescs := myErr;
  270.     END;
  271.  
  272.  
  273. FUNCTION GotRequiredParams (theAppleEvent: AppleEvent): OSErr;
  274. { checks the AppleEvent to see if we've gotten all the required parameters}
  275.     VAR
  276.         myErr: OSErr;
  277.         returnedType: DescType;
  278.         actSize: Size;
  279.     BEGIN
  280.   { look for the keyMissedKeywordAttr, just to see if it's there }
  281.         myErr := AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, returnedType, NIL, 0, actSize);
  282.         IF myErr = errAEDescNotFound THEN
  283.             GotRequiredParams := noErr            { attribute not there means we got all req params }
  284.         ELSE IF myErr = noErr THEN
  285.             GotRequiredParams := errAEParamMissed        { attribute there means missed at least one }
  286.         ELSE
  287.             GotRequiredParams := myErr;    { some unexpected arror in looking for the attribute }
  288.     END;
  289.  
  290. {------------------------------------------------------------------------------------------------}
  291.  
  292.     TYPE
  293.         propActionType = (propGet, propSet);
  294.  
  295.  
  296. FUNCTION TransferProperty (propAction: propActionType; propPtr: ptr; kind: char; lenProp: integer; writeable: Boolean; ae: AppleEvent): OSErr;
  297.     LABEL
  298.         99;
  299.     CONST
  300.         difficult = '????';
  301.     VAR
  302.         descriptor, actualType: DescType;
  303.         buffer: PACKED ARRAY[0..511] OF char;
  304.         err: OSErr;
  305.         strlen, dbx: integer;
  306.         actualSize: Size;
  307.         debugString: str255;
  308.     BEGIN
  309.         IF (kind = 'I') & (lenProp = 2) THEN
  310.             descriptor := typeShortInteger
  311.         ELSE IF (kind = 'I') & (lenProp = 4) THEN
  312.             descriptor := typeLongInteger
  313.         ELSE IF (kind = 'R') & (lenProp = 4) THEN
  314.             descriptor := typeShortFloat
  315.         ELSE IF (kind = 'R') & (lenProp = 8) THEN
  316.             descriptor := typeLongFloat
  317.         ELSE
  318.             descriptor := difficult;    {anything else:  strings, booleans, extended, fileSpec}
  319.  
  320.     {GET DATA}
  321.  
  322.         IF propAction = propGet THEN
  323.             BEGIN
  324.                 {get the value of the specified property}
  325.                 IF lenProp <= SIZEOF(buffer) THEN
  326.                     BlockMove(propPtr, @buffer, lenProp)        {put the contents of the property into the buffer}
  327.                 ELSE
  328.                     BEGIN
  329.                         err := eBufferTooSmall;
  330.                         GOTO 99;
  331.                     END;
  332.  
  333.                 {stuff the value into the AppleEvent}
  334.                 IF descriptor <> difficult THEN
  335.                     err := AEPutParamPtr(ae, keyDirectObject, descriptor, @buffer, lenProp)
  336.                 ELSE IF kind = 'S' THEN
  337.                     BEGIN
  338.                         strlen := ORD(buffer[0]);
  339.                         err := AEPutParamPtr(ae, keyDirectObject, typeChar, @buffer[1], strlen);
  340.                     END
  341.                 ELSE
  342.                     err := eCannotHandleAPropertyOfThisType;
  343.             END
  344.  
  345.     {SET DATA}
  346.  
  347.         ELSE IF propAction = propSet THEN
  348.             BEGIN
  349.                 IF NOT writeable THEN
  350.                     BEGIN
  351.                         err := errAENotModifiable;
  352.                         GOTO 99;
  353.                     END;
  354.  
  355.                 {retrieve the new value from the AppleEvent}
  356.                 IF kind = 'S' THEN
  357.                     BEGIN
  358.                         err := AEGetParamPtr(ae, keyAEData, typeChar, actualType, @buffer[1], SIZEOF(buffer) - 1, actualSize);
  359.                         IF err = noErr THEN
  360.                             BEGIN
  361.                                 strlen := actualSize;
  362.                                 IF strlen > 255 THEN
  363.                                     strlen := 255;
  364.                                 buffer[0] := CHR(strlen);
  365.  
  366.                                 IF (strlen + 1) > lenProp THEN    {too big to fit in a string structure this size}
  367.                                     BEGIN
  368.                                         strlen := lenProp - 1;
  369.                                         buffer[0] := CHR(strlen);
  370.                                     END;
  371.  
  372.                                 BlockMove(@buffer, propPtr, strlen + 1);
  373.                             END;
  374.                     END
  375.                 ELSE IF descriptor <> difficult THEN
  376.                     BEGIN
  377.                         {DisplayParameterInfo(ae, keyAEData);}
  378.                         err := AEGetParamPtr(ae, keyAEData, descriptor, actualType, @buffer, SIZEOF(buffer), actualSize);
  379.                         IF err = noErr THEN
  380.                             BEGIN
  381.                                 IF descriptor <> actualType THEN    {we didn't get what we wanted}
  382.                                     err := ePropertyValueSpecifiedInIncorrectFormat
  383.                                 ELSE IF lenProp <> actualSize THEN
  384.                                     err := ePropertyValueSpecifiedWithIncorrectSize;
  385.  
  386. {$IFC TRUE}
  387.                                 IF err <> noErr THEN
  388.                                     BEGIN
  389.                                         ParamText(descriptor, N2S(lenProp), actualType, N2S(actualSize));
  390.                                         dbx := NoteAlert(7502, NIL);
  391.                                         debugString := STRINGOF(ORD(buffer[0]) : 1, ' ', ORD(buffer[1]) : 1, ' ', ORD(buffer[2]) : 1, ' ', ORD(buffer[3]) : 1, ' ', ORD(buffer[4]) : 1, ' ', ORD(buffer[5]) : 1, ' ', ORD(buffer[6]) : 1, ' ', ORD(buffer[7]) : 1, ' ', ORD(buffer[8]) : 1, ' ', ORD(buffer[9]) : 1);
  392.                                         ParamText('TransferProperty, Set Data:  the first few values in the buffer are: ', debugString, '', '');
  393.                                         dbx := NoteAlert(7500, NIL);
  394.                                     END;
  395. {$ENDC}
  396.                             END;
  397.  
  398.                         IF err = noErr THEN    {everything looks good, so revise the property itself!}
  399.                             BlockMove(@buffer, propPtr, lenProp);
  400.                     END
  401.                 ELSE
  402.                     err := eCannotHandleAPropertyOfThisType;
  403.             END;
  404.  
  405. 99:
  406.         TransferProperty := err;
  407.  
  408. {$IFC CHATTY}
  409.         ParamText('TransferProperty, return err = ', N2S(err), '', '');
  410.         IF NoteAlert(7500, NIL) = 1 THEN
  411.             ;
  412. {$ENDC}
  413.     END;
  414.  
  415.  
  416. FUNCTION DoTransferProperty (propAction: propActionType; VAR myToken: MyTokenType; ae: AppleEvent): OSErr;
  417.     VAR
  418.         obj: CObject;
  419.         thisRow: KRow;
  420.         doc: KTHISAPPDoc;
  421.         err: OSErr;
  422.         oldLock, recalc: Boolean;
  423.         prop: DescType;
  424.         sss: str255;
  425.     BEGIN
  426.         prop := myToken.propertyCode;
  427.         obj := myToken.theObject;
  428.         oldLock := obj.Lock(TRUE);
  429.         recalc := FALSE;
  430.  
  431.     {APPLICATION}
  432.         IF myToken.myTokenCode = typeNull THEN
  433.             BEGIN
  434.                 IF prop = 'pnam' THEN
  435.                     err := TransferProperty(propAction, @gAppName, 'S', SIZEOF(gAppName), FALSE, ae)
  436.                 ELSE IF prop = 'pcli' THEN
  437.                     BEGIN
  438.                         IF gClipboard.GetString(sss) THEN
  439.                             err := TransferProperty(propAction, @sss, 'S', SIZEOF(sss), FALSE, ae);
  440.                     END
  441.                 ELSE IF prop = 'vers' THEN
  442.                     BEGIN
  443.                         sss := '«Version number should be determined here»';
  444.                         err := TransferProperty(propAction, @sss, 'S', SIZEOF(sss), FALSE, ae);
  445.                     END
  446.                 ELSE
  447.                     err := eThisPropertyUnderConstruction;
  448.             END
  449.  
  450.     {WINDOW}
  451.         ELSE IF myToken.myTokenCode = winTokenCode THEN
  452.             err := eThisClassUnderConstruction
  453.  
  454.     {DOCUMENT}
  455.         ELSE IF myToken.myTokenCode = docTokenCode THEN    {handle doc properties for THIS APP!}
  456.             BEGIN
  457.                 doc := KTHISAPPDoc(obj);
  458.  
  459.                 IF prop = 'tgrp' THEN
  460.                     err := TransferProperty(propAction, @doc.docgrp, 'R', SIZEOF(doc.docgrp), FALSE, ae)
  461.                 ELSE IF prop = 'ggrp' THEN
  462.                     err := TransferProperty(propAction, @doc.docgoalgrp, 'R', SIZEOF(doc.docgoalgrp), FALSE, ae)
  463.                 ELSE IF prop = 'tcst' THEN
  464.                     err := TransferProperty(propAction, @doc.doccost, 'R', SIZEOF(doc.doccost), FALSE, ae)
  465.                 ELSE IF prop = 'tbud' THEN
  466.                     err := TransferProperty(propAction, @doc.docbudget, 'R', SIZEOF(doc.docbudget), FALSE, ae)
  467.                 ELSE IF prop = 'tins' THEN
  468.                     err := TransferProperty(propAction, @doc.docUnits, 'R', SIZEOF(doc.docUnits), FALSE, ae)
  469.                 ELSE IF prop = '*gwt' THEN
  470.                     err := TransferProperty(propAction, @doc.geoWeight, 'R', SIZEOF(doc.geoWeight), FALSE, ae)
  471.                 ELSE
  472.                     err := eThisClassUnderConstruction;
  473.             END
  474.  
  475.     {ROWS}
  476.         ELSE IF myToken.myTokenCode = rowTokenCode THEN    {handle row properties for THIS APP!}
  477.             BEGIN
  478.                 thisRow := KRow(obj);
  479.  
  480.                 IF prop = '*mht' THEN
  481.                     err := TransferProperty(propAction, @thisRow.height, 'I', SIZEOF(thisRow.height), TRUE, ae)
  482.                 ELSE IF prop = 'flpt' THEN
  483.                     err := TransferProperty(propAction, @thisRow.fillPat, 'I', SIZEOF(thisRow.fillPat), TRUE, ae)
  484.                 ELSE IF prop = 'pppa' THEN
  485.                     err := TransferProperty(propAction, @thisRow.linePat, 'I', SIZEOF(thisRow.linePat), TRUE, ae)
  486.                 ELSE IF prop = 'flcl' THEN
  487.                     err := TransferProperty(propAction, @thisRow.fillCol, 'I', SIZEOF(thisRow.fillCol), TRUE, ae)
  488.                 ELSE IF prop = 'ppcl' THEN
  489.                     err := TransferProperty(propAction, @thisRow.lineCol, 'I', SIZEOF(thisRow.lineCol), TRUE, ae)
  490.                 ELSE IF prop = 'ppwd' THEN
  491.                     err := TransferProperty(propAction, @thisRow.lineThick, 'I', SIZEOF(thisRow.lineThick), TRUE, ae)
  492.                 ELSE IF prop = 'pnam' THEN
  493.                     err := TransferProperty(propAction, PTR(thisRow.title^), 'S', SIZEOF(thisRow.title^^), TRUE, ae)
  494.                 ELSE IF prop = 'desc' THEN
  495.                     err := TransferProperty(propAction, PTR(thisRow.subtitle^), 'S', SIZEOF(thisRow.subtitle^^), TRUE, ae)
  496.                 ELSE IF prop = 'twks' THEN
  497.                     err := TransferProperty(propAction, @thisRow.nweeks, 'I', SIZEOF(thisRow.nweeks), FALSE, ae)
  498.                 ELSE IF prop = 'tgrp' THEN
  499.                     err := TransferProperty(propAction, @thisRow.mgrp, 'R', SIZEOF(thisRow.mgrp), FALSE, ae)
  500.                 ELSE IF prop = 'ggrp' THEN
  501.                     err := TransferProperty(propAction, @thisRow.mgoalgrp, 'R', SIZEOF(thisRow.mgoalgrp), FALSE, ae)
  502.                 ELSE IF prop = 'tcst' THEN
  503.                     err := TransferProperty(propAction, @thisRow.mcost, 'R', SIZEOF(thisRow.mcost), FALSE, ae)
  504.                 ELSE IF prop = 'tbud' THEN
  505.                     err := TransferProperty(propAction, @thisRow.mbudget, 'R', SIZEOF(thisRow.mbudget), FALSE, ae)
  506.                 ELSE IF prop = 'tins' THEN
  507.                     err := TransferProperty(propAction, @thisRow.mUnits, 'R', SIZEOF(thisRow.mUnits), FALSE, ae)
  508.                 ELSE IF prop = '*gwt' THEN
  509.                     err := TransferProperty(propAction, @thisRow.geoWeight, 'R', SIZEOF(thisRow.geoWeight), FALSE, ae)
  510.                 ELSE
  511.                     err := eThisPropertyUnderConstruction;
  512.  
  513.                 IF (propAction = propSet) & (err = noErr) THEN
  514.                     thisRow.Refresh;
  515.             END
  516.  
  517.  
  518.         ELSE
  519.             err := eCannotHandlePropertiesOfThisClass;
  520.  
  521.         oldLock := obj.Lock(oldLock);
  522.         DoTransferProperty := err;
  523.     END;
  524.  
  525. {------------------------------------------------------------------------------------------------}
  526.  
  527. FUNCTION HandleGetData (theAppleEvent: AppleEvent; reply: AppleEvent; handlerRefCon: LongInt): OSErr;
  528.     LABEL
  529.         9;
  530.     VAR
  531.         myToken: MyTokenType;
  532.         item: integer;
  533.         siz: longint;
  534.         err: OSErr;
  535.         myDirObj: AEDesc;
  536.         reqType: DescType;
  537.         reqTypesList: AEDesc;
  538.         newDesc: AEDesc;
  539.         notToken: BOOLEAN;    { really, we ignore this one }
  540.         dataDesc: AEDesc;
  541.     BEGIN
  542. {$IFC CHATTY}
  543.         ParamText('HandleGetData', ' - START', '', '');
  544.         item := NoteAlert(7500, NIL);
  545. {$ENDC}
  546.  
  547.         PreHandler;
  548.         err := errAEEventNotHandled;
  549.         InitSomeDescs(@myDirObj, @dataDesc, @reqTypesList, NIL, NIL);
  550.  
  551.   { pick up the direct object }
  552.  
  553.         IF CatchErr(AEGetParamDesc(theAppleEvent, keyDirectObject, typeWildCard, myDirObj), 14613, err) THEN
  554.             GOTO 9;    { finish up }
  555.  
  556.   { get a requested return type list, if any }
  557.         err := AEGetParamPtr(theAppleEvent, keyAERequestedType, typeAEList, gReturnedType, @reqType, SizeOf(reqType), gActSize);
  558.  
  559.   { NOTE: all lower-level routines treat a reqTypesList of typeNull as though it }
  560.   { were a 1-element list containing typeWildCard, so we don't have to hoke up   }
  561.   { a 1-element list here                                                        }
  562.  
  563.         IF err = errAEDescNotFound THEN
  564.             err := noErr
  565.         ELSE IF err <> noErr THEN    { unexpected problem while trying to get param }
  566.             BEGIN
  567.                 gTempBool := CheckErr(err, 14614);
  568.                 GOTO 9;
  569.             END;
  570.  
  571.   { check for required parameters }
  572.         IF CatchErr(GotRequiredParams(theAppleEvent), 14615, err) THEN
  573.             GOTO 9;
  574.  
  575.     {RESOLVE}
  576.         IF CatchErr(AEResolve(myDirObj, kAEIDoMinimum, newDesc), 14620, err) THEN
  577.             GOTO 9;
  578.         BlockMove(newDesc.dataHandle^, @myToken, myTokenSize);
  579.  
  580. {$IFC CHATTY}
  581.         ParamText('RESOLVED!  my token code is ', myToken.myTokenCode, ', propertyCode is ', myToken.propertyCode);
  582.         item := NoteAlert(7500, NIL);
  583. {$ENDC}
  584.  
  585.     {GET THE DESIRED PROPERTY, AND RETURN IT}
  586.         err := DoTransferProperty(propGet, myToken, reply);
  587.  
  588.  
  589. 9:    { finish up }
  590.  
  591.         gTempBool := CheckErr(DisposeSomeDescs(@myDirObj, @dataDesc, @reqTypesList, NIL, NIL), 14619);
  592.         HandleGetData := err;
  593.         PostHandler(reply, err);
  594.  
  595. {$IFC CHATTY}
  596.         ParamText('HandleGetData', ' - DONE', '', '');
  597.         item := NoteAlert(7500, NIL);
  598. {$ENDC}
  599.     END;
  600.  
  601.  
  602. FUNCTION HandleSetData (theAppleEvent: AppleEvent; reply: AppleEvent; handlerRefCon: LongInt): OSErr;
  603.     LABEL
  604.         9;
  605.     VAR
  606.         myToken: MyTokenType;
  607.         item: integer;
  608.         err: OSErr;
  609.         myDirObj: AEDesc;
  610.         myDataDesc: AEDesc;
  611.         newDesc: AEDesc;
  612.     BEGIN
  613. {$IFC CHATTY}
  614.         ParamText('HandleSetData', ' - START', '', '');
  615.         item := NoteAlert(7500, NIL);
  616. {$ENDC}
  617.  
  618.         PreHandler;
  619.         err := errAEEventNotHandled;
  620.         InitSomeDescs(@myDirObj, @myDataDesc, @newDesc, NIL, NIL);
  621.  
  622.   { pick up the direct object, which is the object whose data is to be set }
  623.         IF CatchErr(AEGetParamDesc(theAppleEvent, keyDirectObject, typeWildCard, myDirObj), 15013, err) THEN
  624.             GOTO 9;    { finish up }
  625.  
  626.  
  627.     {RESOLVE}
  628.         IF CatchErr(AEResolve(myDirObj, kAEIDoMinimum, newDesc), 15017, err) THEN
  629.             GOTO 9;
  630.         BlockMove(newDesc.dataHandle^, @myToken, myTokenSize);
  631.  
  632.     {GET THE DESIRED PROPERTY, AND SET IT, according to data contents of the AppleEvent}
  633.         err := DoTransferProperty(propSet, myToken, theAppleEvent);
  634.  
  635.  
  636. 9:    { finish up }
  637.         gTempBool := CheckErr(DisposeSomeDescs(@myDirObj, @myDataDesc, @newDesc, NIL, NIL), 15020);
  638.  
  639.         HandleSetData := err;
  640.         PostHandler(reply, err);
  641.  
  642. {$IFC CHATTY}
  643.         ParamText('HandleSetData', ' - DONE', '', '');
  644.         item := NoteAlert(7500, NIL);
  645. {$ENDC}
  646.     END;
  647.  
  648. {------------------------------------------------------------------------------------------------}
  649.  
  650. FUNCTION MyAECoerceDescPtr (theAEDesc: AEDesc; toType: DescType; dataPtr: Ptr; maximumSize: Size; VAR actualSize: Size): OSErr;
  651. { this routine plugs a hole that's been nagging at me in the AppleEvents}
  652. {  interface.  It takes a descriptor and coerces it to a desired type; but}
  653. {  instead of returning a descriptor, it returns data in a buffer specified}
  654. {  by the caller.}
  655. {  INPUTS:    theAEDesc        descriptor to be coerced}
  656. {              toType            type to coerce it to}
  657. {            dataPtr            ptr to data buffer}
  658. {            maximumSize        maximum length in bytes of data to be returned}
  659. {            actualSize        actual length in bytes of data for the descriptor}
  660. {  OUTPUTS:    error code (noErr if none)}
  661. {  ERRORS:}
  662. {  SIDE EFFECTS:}
  663. {  NOTES:    12/16/91    BHM        (1) Changed to avoid unecessary duplication when the type}
  664. {                                  doesn't really change (this should also enable it to handle}
  665. {                                typeWildCard better)}
  666. {                                (2) We don't need to dispose of newDesc because it is a direct}
  667. {                                copy (not a duplicate) of either theAEDesc or resultDesc - that}
  668. {                                is, it contains the same handle}
  669. {}
  670.     LABEL
  671.         9;
  672.     VAR
  673.         myErr: INTEGER;
  674.         newDesc: AEDesc;
  675.         resultDesc: AEDesc;
  676.         transferSize: Size;
  677.     BEGIN
  678.         myErr := errAECoercionFail;
  679.         resultDesc := gNullDesc;
  680.  
  681.   { to avoid unnecessary duplication, check old type vs. new type }
  682.         IF (theAEDesc.descriptorType = toType) OR (toType = typeWildCard) THEN
  683.             newDesc := theAEDesc
  684.         ELSE
  685.             BEGIN    { must coerce to new type }
  686.                 IF QuietCatchErr(AECoerceDesc(theAEDesc, toType, resultDesc), myErr) THEN
  687.                     GOTO 9;
  688.                 newDesc := resultDesc;
  689.             END;
  690.  
  691.         WITH newDesc DO
  692.             BEGIN { get the size }
  693.                 actualSize := GetHandleSize(dataHandle);
  694.                 IF QuietCatchErr(MemError, myErr) THEN
  695.                     GOTO 9;
  696.  
  697.                 { calculate number of bytes to move }
  698.                 transferSize := actualSize;
  699.                 IF maximumSize < transferSize THEN
  700.                     transferSize := maximumSize;
  701.  
  702.                 { move the data }
  703.                 HLock(dataHandle);
  704.                 BlockMove(dataHandle^, dataPtr, transferSize);
  705.                 HUnlock(dataHandle);
  706.             END;    { of WITH newDesc }
  707.  
  708.   { everything fine }
  709.         myErr := noErr;
  710.  
  711. 9:    { finish up }
  712.         gTempBool := CheckErr(AEDisposeDesc(resultDesc), 2215);
  713.         MyAECoerceDescPtr := myErr;
  714.     END;
  715.  
  716.  
  717. FUNCTION TextDescToStr (textDesc: AEDesc; VAR destStr: Str255; VAR actSize: Size): OSErr;
  718. { this routine takes a descriptor that contains text information}
  719. {  (basically, anything that can be coerced to typeChar) and copies}
  720. {  the text into a Pascal string.  The text will be truncated to 255 }
  721. {  characters, if necessary; the actual size of the original text will}
  722. {  also be returned.}
  723. {  INPUTS:    textDesc    the descriptor containing the text}
  724. {              destStr        return VAR for the string}
  725. {            actSize        return VAR for the actual text length}
  726. {  OUTPUTS:    error code (noErr if none).  Truncation is not an error.}
  727.     LABEL
  728.         9;
  729.     VAR
  730.         myErr: OSErr;
  731.         destStrPtr: Ptr;
  732.         xferSize: Size;
  733.     BEGIN
  734.         myErr := genericErr;
  735.         actSize := 0;
  736.         destStr := 'bad string';
  737.  
  738.         destStrPtr := Ptr(ORD4(@destStr) + 1);
  739.         IF CatchErr(MyAECoerceDescPtr(textDesc, typeChar, destStrPtr, 255, actSize), 9013, myErr) THEN
  740.             GOTO 9;    { set function result }
  741.  
  742.         xferSize := actSize;
  743.         IF xferSize > 255 THEN
  744.             xferSize := 255;
  745.         destStrPtr := @destStr;
  746.         destStrPtr^ := xferSize;
  747.  
  748.         myErr := noErr;
  749.  
  750. 9:    { set function result }
  751.         TextDescToStr := myErr;
  752.     END;
  753.  
  754.  
  755. FUNCTION AppObjectAccessor (desiredClass: DescType; containerToken: AEDesc; keyForm: DescType; keyData: AEDesc; VAR theTokenBody: MyTokenType): OSErr;
  756.     VAR
  757.         err, numDocs: integer;
  758.         actSize: Size;
  759.         wantedName, sss: str255;
  760.         wantedIndex: longint;
  761.         w: WindowPeek;
  762.         obj: CObject;
  763.         found: Boolean;
  764.     PROCEDURE Bail (bailErr: integer);
  765.         BEGIN
  766.             AppObjectAccessor := bailErr;
  767.             EXIT(AppObjectAccessor);
  768.         END;
  769.     BEGIN
  770.         IF (desiredClass = 'cwin') | (desiredClass = 'docu') THEN
  771.             BEGIN
  772.                 IF keyForm = formName THEN
  773.                     BEGIN
  774.                         IF CatchErr(TextDescToStr(keyData, wantedName, actSize), 1915, err) THEN
  775.                             Bail(err);
  776.                     END
  777.  
  778.                 ELSE IF keyForm = formAbsolutePosition THEN
  779.                     BEGIN
  780.                         numDocs := 0;
  781.                         w := WindowPeek(FrontWindow);
  782.                         WHILE w <> NIL DO
  783.                             BEGIN
  784.                                 IF w^.windowKind = OBJ_WINDOW_KIND THEN
  785.                                     BEGIN
  786.                                         obj := CWindow(GetWRefCon(WindowPtr(w)));
  787.                                         IF Member(CBureaucrat(obj).itsSupervisor, KTHISAPPDoc) THEN
  788.                                             numDocs := numDocs + 1;
  789.                                     END;
  790.                                 w := w^.nextWindow;
  791.                             END;
  792.  
  793.                         wantedIndex := LongHandle(keyData.dataHandle)^^;
  794.                         IF keyData.descriptorType = typeLongInteger THEN
  795.                             BEGIN
  796.                                 IF wantedIndex <= 0 THEN
  797.                                     wantedIndex := numDocs + wantedIndex + 1;
  798.                             END
  799.                         ELSE IF keyData.descriptorType = typeAbsoluteOrdinal THEN
  800.                             BEGIN
  801.                                 IF wantedIndex = LONGINT(kAEFirst) THEN
  802.                                     wantedIndex := 1
  803.                                 ELSE IF wantedIndex = LONGINT(kAELast) THEN
  804.                                     wantedIndex := numDocs
  805.                                 ELSE
  806.                                     Bail(eOnlyFirstOrLast);
  807.                             END
  808.                         ELSE
  809.                             Bail(eOnlyNameIndexFirstOrLast);
  810.  
  811.                         IF (wantedIndex < 1) | (wantedIndex > numDocs) THEN
  812.                             Bail(eIndexNumberOutOfRange);
  813.                     END
  814.                 ELSE
  815.                     Bail(eOnlyNameOrIndex);
  816.  
  817.                         {we now know what is wanted, let's loop through and see if we can find it}
  818.                 numDocs := 0;
  819.                 found := FALSE;
  820.                 w := WindowPeek(FrontWindow);
  821.                 WHILE (w <> NIL) & (NOT found) DO
  822.                     BEGIN
  823.                         IF w^.windowKind = OBJ_WINDOW_KIND THEN
  824.                             BEGIN
  825.                                 obj := CWindow(GetWRefCon(WindowPtr(w)));
  826.                                 IF Member(CBureaucrat(obj).itsSupervisor, KTHISAPPDoc) THEN
  827.                                     BEGIN
  828.                                         numDocs := numDocs + 1;
  829.                                         IF keyForm = formAbsolutePosition THEN
  830.                                             found := (numDocs = wantedIndex)
  831.                                         ELSE IF keyForm = formName THEN
  832.                                             BEGIN
  833.                                                 CWindow(obj).GetTitle(sss);
  834.                                                 found := (sss = wantedName);
  835.                                             END;
  836.  
  837.                                         IF found THEN
  838.                                             BEGIN
  839.                                                 IF desiredClass = 'cwin' THEN
  840.                                                     BEGIN
  841.                                                         theTokenBody.myTokenCode := winTokenCode;
  842.                                                         theTokenBody.theObject := CObject(obj);
  843.                                                     END
  844.                                                 ELSE IF desiredClass = 'docu' THEN
  845.                                                     BEGIN
  846.                                                         theTokenBody.myTokenCode := docTokenCode;
  847.                                                         theTokenBody.theObject := CObject(CBureaucrat(obj).itsSupervisor);
  848.                                                     END;
  849.                                                 theTokenBody.subReference := 0;
  850.                                                 theTokenBody.isAProperty := FALSE;
  851.                                                 Bail(noErr);
  852.                                             END;
  853.                                     END;
  854.                             END;
  855.                         w := w^.nextWindow;
  856.                     END;
  857.  
  858.                 {fell thru the loop without success}
  859.                 Bail(errAENoSuchObject);
  860.             END
  861.         ELSE
  862.             Bail(eContainerDoesNotContainRequestedClass);
  863.     END;
  864.  
  865.  
  866. FUNCTION GetTokenFromAEDesc (ref: char; theAEDesc: AEDesc; VAR theToken: MyTokenType): Boolean;
  867.     VAR
  868.         gotIt: Boolean;
  869.         sss: str255;
  870.         siz: longint;
  871.     BEGIN
  872.         gotIt := FALSE;
  873.         IF theAEDesc.descriptorType <> typeNull THEN
  874.             IF theAEDesc.dataHandle <> NIL THEN
  875.                 IF GetHandleSize(theAEDesc.dataHandle) = myTokenSize THEN
  876.                     BEGIN
  877.                         BlockMove(theAEDesc.dataHandle^, @theToken, myTokenSize);
  878.                         gotIt := TRUE;
  879.                     END;
  880.  
  881. {$IFC CHATTY}
  882.         IF NOT gotIt THEN
  883.             BEGIN
  884.                 sss := STRINGOF(ref, '—GetTokenFromAEDesc:  type “', theAEDesc.descriptorType);
  885.                 IF theAEDesc.dataHandle = NIL THEN
  886.                     ParamText(sss, '”, handle NIL', '', '')
  887.                 ELSE
  888.                     ParamText(sss, '”, handle  size ', N2S(GetHandleSize(theAEDesc.dataHandle)), '');
  889.                 IF NoteAlert(7500, NIL) = 1 THEN
  890.                     ;
  891.  
  892.                 siz := GetHandleSize(theAEDesc.dataHandle);
  893.                 IF siz <= 255 THEN
  894.                     BEGIN
  895.                         BlockMove(theAEDesc.dataHandle^, @sss[1], siz);
  896.                         sss[0] := CHR(siz);
  897.                         ParamText('Contents of that handle as a string: “', sss, '”', '');
  898.                         IF NoteAlert(7500, NIL) = 1 THEN
  899.                             ;
  900.                     END;
  901.             END;
  902. {$ENDC}
  903.  
  904.         GetTokenFromAEDesc := gotIt;
  905.     END;
  906.  
  907.  
  908. FUNCTION DocObjectAccessor (desiredClass: DescType; containerToken: AEDesc; keyForm: DescType; keyData: AEDesc; VAR theTokenBody: MyTokenType): OSErr;
  909.     VAR
  910.         err, nmedia, mx: integer;
  911.         actSize: Size;
  912.         wantedName, sss: str255;
  913.         wantedIndex: longint;
  914.         obj: CObject;
  915.         found: Boolean;
  916.         docToken: MyTokenType;
  917.         doc: KTHISAPPDoc;
  918.         listOfRows: CList;
  919.         thisRow: KRow;
  920.     PROCEDURE Bail (bailErr: integer);
  921.         BEGIN
  922.             DocObjectAccessor := bailErr;
  923.             EXIT(DocObjectAccessor);
  924.         END;
  925.     BEGIN
  926.         IF NOT GetTokenFromAEDesc('D', containerToken, docToken) THEN
  927.             Bail(eContainerDoesNotHaveValidToken);
  928.  
  929.         IF desiredClass = 'crow' THEN
  930.             {good}
  931.         ELSE
  932.             Bail(eContainerDoesNotContainRequestedClass);
  933.  
  934.         doc := KTHISAPPDoc(docToken.theObject);
  935.         listOfRows := doc.listOfRows;
  936.         nmedia := listOfRows.GetNumItems;
  937.         found := FALSE;
  938.  
  939.         IF keyForm = formName THEN
  940.             BEGIN
  941.                 IF CatchErr(TextDescToStr(keyData, wantedName, actSize), 1915, err) THEN
  942.                     Bail(err);
  943.                 FOR mx := 1 TO nmedia DO
  944.                     BEGIN
  945.                         thisRow := KRow(listOfRows.NthItem(mx));
  946.                         IF thisRow.title^^ = wantedName THEN    {GET DESIRED ROW, BY NAME}
  947.                             BEGIN
  948.                                 found := TRUE;
  949.                                 LEAVE;
  950.                             END;
  951.                     END;
  952.             END
  953.  
  954.         ELSE IF keyForm = formAbsolutePosition THEN
  955.             BEGIN
  956.                 wantedIndex := LongHandle(keyData.dataHandle)^^;
  957.                 IF keyData.descriptorType = typeLongInteger THEN
  958.                     BEGIN
  959.                         IF wantedIndex <= 0 THEN
  960.                             wantedIndex := nmedia + wantedIndex + 1;
  961.                     END
  962.                 ELSE IF keyData.descriptorType = typeAbsoluteOrdinal THEN
  963.                     BEGIN
  964.                         IF wantedIndex = LONGINT(kAEFirst) THEN
  965.                             wantedIndex := 1
  966.                         ELSE IF wantedIndex = LONGINT(kAELast) THEN
  967.                             wantedIndex := nmedia
  968.                         ELSE
  969.                             Bail(eOnlyFirstOrLast);
  970.                     END
  971.                 ELSE
  972.                     Bail(eOnlyNameIndexFirstOrLast);
  973.  
  974.                 IF (wantedIndex < 1) | (wantedIndex > nmedia) THEN
  975.                     Bail(eIndexNumberOutOfRange);
  976.  
  977.                 thisRow := KRow(listOfRows.NthItem(wantedIndex));    {GET DESIRED ROW, BY INDEX}
  978.                 found := TRUE;
  979.             END
  980.         ELSE
  981.             Bail(eOnlyNameIndexFirstOrLast);
  982.  
  983.         IF found THEN
  984.             BEGIN
  985.                 theTokenBody.myTokenCode := rowTokenCode;
  986.                 theTokenBody.theObject := CObject(thisRow);
  987.                 theTokenBody.subReference := 0;
  988.                 theTokenBody.isAProperty := FALSE;
  989.                 Bail(noErr);
  990.             END
  991.         ELSE
  992.             Bail(errAENoSuchObject);
  993.     END;
  994.  
  995.  
  996. FUNCTION PropertyAccessor (desiredClass: DescType; containerToken: AEDesc; keyData: AEDesc; VAR theTokenBody: MyTokenType): OSErr;
  997.     VAR
  998.         item: integer;
  999.         siz: longint;
  1000.         propertyCode: DescType;
  1001.     PROCEDURE Bail (bailErr: integer);
  1002.         BEGIN
  1003.             PropertyAccessor := bailErr;
  1004.             EXIT(PropertyAccessor);
  1005.         END;
  1006.     BEGIN
  1007.         IF containerToken.descriptorType = typeNull THEN
  1008.             BEGIN    {container is the app (which doesn't have a token of its own), so make a token for the property}
  1009.                 theTokenBody.myTokenCode := typeNull;
  1010.                 theTokenBody.theObject := NIL;
  1011.                 theTokenBody.subReference := 0;
  1012.             END
  1013.         ELSE IF NOT GetTokenFromAEDesc('P', containerToken, theTokenBody) THEN
  1014.             Bail(eContainerDoesNotHaveValidToken);
  1015.  
  1016.         BlockMove(keyData.dataHandle^, @propertyCode, 4);
  1017.  
  1018.         theTokenBody.isAProperty := TRUE;
  1019.         theTokenBody.propertyCode := propertyCode;
  1020.         PropertyAccessor := noErr;
  1021.     END;
  1022.  
  1023.  
  1024. FUNCTION MyObjectAccessor (desiredClass: DescType; containerToken: AEDesc; containerClass: DescType; {}
  1025.                                 keyForm: DescType; keyData: AEDesc; {}
  1026.                                 VAR theToken: AEDesc; theRefCon: longint): OSErr;
  1027.     VAR
  1028.         err, item: integer;
  1029.     BEGIN
  1030. {$IFC CHATTY}
  1031.         ParamText(containerClass, desiredClass, keyForm, N2S(theRefCon));
  1032.         item := NoteAlert(7501, NIL);
  1033. {$ENDC}
  1034.  
  1035.         {if formPropertyID, we need to identify a specific property of the given object}
  1036.  
  1037.         IF keyForm = formPropertyID THEN
  1038.             err := PropertyAccessor(desiredClass, containerToken, keyData, aTokenBody)
  1039.  
  1040.         {otherwise, we are looking for an object "contained" in the given object}
  1041.  
  1042.         ELSE IF containerClass = typeNull THEN
  1043.             err := AppObjectAccessor(desiredClass, containerToken, keyForm, keyData, aTokenBody)
  1044.         ELSE IF containerClass = 'docu' THEN
  1045.             err := DocObjectAccessor(desiredClass, containerToken, keyForm, keyData, aTokenBody)
  1046.         {*** ADD SUPPORT FOR MORE CLASSES HERE, as needed ***)
  1047.         ELSE
  1048.             err := errAECantHandleClass;
  1049.  
  1050.         IF err = noErr THEN
  1051.             err := AECreateDesc(desiredClass, @aTokenBody, myTokenSize, theToken);
  1052.         MyObjectAccessor := err;
  1053.     END;
  1054.  
  1055. {------------------------------------------------------------------------------------------------}
  1056.  
  1057. FUNCTION MyCounter (desiredClass: DescType; containerDesc: AEDesc; VAR num: longint): OSErr;
  1058.     VAR
  1059.         myToken: MyTokenType;
  1060.         w: WindowPeek;
  1061.         numDoc, numWin, err, item: integer;
  1062.         obj, containerObject: CObject;
  1063.     BEGIN
  1064. {$IFC CHATTY}
  1065.         ParamText('MyCounter, containerDesc=“', containerDesc.descriptorType, '”', '');
  1066.         item := NoteAlert(7500, NIL);
  1067. {$ENDC}
  1068.  
  1069.         num := 0;
  1070.         IF containerDesc.descriptorType = typeNull THEN
  1071.             BEGIN    {container is the app (which doesn't have a token of its own)}
  1072.                 myToken.myTokenCode := typeNull;
  1073.                 myToken.theObject := NIL;
  1074.                 myToken.subReference := 0;
  1075.             END
  1076.         ELSE IF NOT GetTokenFromAEDesc('C', containerDesc, myToken) THEN
  1077.             BEGIN
  1078.                 MyCounter := eContainerDoesNotHaveValidToken;
  1079.                 EXIT(MyCounter);
  1080.             END;
  1081.  
  1082.     {DOCUMENTS and WINDOWS}
  1083.         IF (desiredClass = 'docu') | (desiredClass = 'cwin') THEN
  1084.             BEGIN
  1085.                 numDoc := 0;
  1086.                 numWin := 0;
  1087.                 w := WindowPeek(FrontWindow);
  1088.                 WHILE w <> NIL DO
  1089.                     IF w^.windowKind = OBJ_WINDOW_KIND THEN
  1090.                         BEGIN
  1091.                             obj := CWindow(GetWRefCon(WindowPtr(w)));
  1092.                             numWin := numWin + 1;
  1093.                             IF Member(CBureaucrat(obj).itsSupervisor, KTHISAPPDoc) THEN
  1094.                                 numDoc := numDoc + 1;
  1095.                             w := w^.nextWindow;
  1096.                         END;
  1097.                 IF desiredClass = 'docu' THEN
  1098.                     num := numDoc
  1099.                 ELSE IF desiredClass = 'cwin' THEN
  1100.                     num := numWin;
  1101.                 err := noErr;
  1102.             END
  1103.  
  1104.     {ROWS}
  1105.         ELSE IF desiredClass = 'crow' THEN
  1106.             BEGIN
  1107.                 containerObject := myToken.theObject;
  1108.                 IF Member(containerObject, KTHISAPPDoc) THEN
  1109.                     BEGIN
  1110.                         IF KTHISAPPDoc(containerObject).listOfRows = NIL THEN
  1111.                             num := 0
  1112.                         ELSE
  1113.                             num := KTHISAPPDoc(containerObject).listOfRows.GetNumItems;
  1114.                         err := noErr;
  1115.                     END
  1116.                 ELSE
  1117.                     err := eElementIsNotMemberOfSpecifiedContainer;
  1118.             END
  1119.  
  1120.     {UNKNOWN}
  1121.         ELSE
  1122.             err := errAECantHandleClass;
  1123.  
  1124.         MyCounter := err;
  1125.     END;
  1126.  
  1127.  
  1128. FUNCTION CountCallback (desiredClass: DescType; containerClass: DescType; containerToken: AEDesc; VAR num: longint): OSErr;
  1129.     VAR
  1130.         item: integer;
  1131.     BEGIN
  1132. {$IFC CHATTY}
  1133.         ParamText('CountCallback for container class=', containerClass, ' and type=', containerToken.descriptorType);
  1134.         item := NoteAlert(7500, NIL);
  1135. {$ENDC}
  1136.  
  1137.         CountCallback := MyCounter(desiredClass, containerToken, num);
  1138.     END;
  1139.  
  1140.  
  1141. FUNCTION HandleCountElements (theAppleEvent: AppleEvent; reply: AppleEvent; handlerRefCon: LongInt): OSErr;
  1142.     LABEL
  1143.         9;
  1144.     VAR
  1145.         myErr: OSErr;
  1146.         myDirObj, newDesc: AEDesc;
  1147.         myClass: DescType;
  1148.         myCount: LongInt;
  1149.         myToken: MyTokenType;
  1150.         item: integer;
  1151.     BEGIN
  1152.         PreHandler;
  1153.         myErr := errAEEventNotHandled;
  1154.         myDirObj := gNullDesc;
  1155.  
  1156.   { pick up direct object, which is the container in which things are to be counted }
  1157.         IF CatchErr(AEGetParamDesc(theAppleEvent, keyDirectObject, typeWildCard, myDirObj), 17913, myErr) THEN
  1158.             GOTO 9;
  1159.  
  1160. {$IFC CHATTY}
  1161.         ParamText('HandleCountElements, the direct object (container) is type “', myDirObj.descriptorType, '”', '');
  1162.         item := NoteAlert(7500, NIL);
  1163. {$ENDC}
  1164.  
  1165.     {RESOLVE}
  1166.         IF myDirObj.descriptorType = typeObjectSpecifier THEN
  1167.             BEGIN
  1168.                 IF CatchErr(AEResolve(myDirObj, kAEIDoMinimum, newDesc), 17914, myErr) THEN
  1169.                     GOTO 9;
  1170. {$IFC CHATTY}
  1171.                 ParamText('We resolved this to type “', newDesc.descriptorType, '”', '');
  1172.                 item := NoteAlert(7500, NIL);
  1173. {$ENDC}
  1174.             END
  1175.         ELSE
  1176.             newDesc := myDirObj;
  1177. { BlockMove(newDesc.dataHandle^, @myToken, myTokenSize); }
  1178.  
  1179.   { now the class of objects to be counted }
  1180.         IF CatchErr(AEGetParamPtr(theAppleEvent, keyAEObjectClass, typeType, gReturnedType, @myClass, SizeOf(myClass), gActSize), 17915, myErr) THEN
  1181.             GOTO 9;
  1182.  
  1183.   { missing any parameters? }
  1184.         IF CatchErr(GotRequiredParams(theAppleEvent), 17916, myErr) THEN
  1185.             GOTO 9;
  1186.  
  1187.   { now count }
  1188.         IF CatchErr(MyCounter(myClass, newDesc, myCount), 17917, myErr) THEN        {myDirObj? newDesc?}
  1189.             GOTO 9;
  1190.  
  1191.   { add result to reply }
  1192.         IF reply.descriptorType <> typeNull THEN
  1193.             gTempBool := CatchErr(AEPutParamPtr(reply, keyDirectObject, typeLongInteger, @myCount, SizeOf(myCount)), 17918, myErr);
  1194.  
  1195. 9:    { finish up }
  1196.  
  1197.         gTempBool := CheckErr(AEDisposeDesc(myDirObj), 17919);
  1198.  
  1199.         HandleCountElements := myErr;
  1200.         PostHandler(reply, myErr);
  1201.     END;
  1202.  
  1203. {------------------------------------------------------------------------------------------------}
  1204.  
  1205. {We have received a message from BASIC}
  1206.  
  1207. FUNCTION MessageFromBasic (theAppleEvent: AppleEvent; reply: AppleEvent; handlerRefCon: LongInt): OSErr;
  1208.     VAR
  1209.         desiredClass: DescType;
  1210.         containerToken: AEDesc;
  1211.         containerClass: DescType;
  1212.         keyForm: DescType;
  1213.         keyData: AEDesc;
  1214.         theToken: AEDesc;
  1215.         theRefCon: longint;
  1216.         err: integer;
  1217.     BEGIN
  1218.         MessageFromBasic := 1234;    {an obvious err code for now}
  1219.  
  1220.         IF FALSE THEN
  1221.             err := MyObjectAccessor(desiredClass, containerToken, containerClass, keyForm, keyData, theToken, theRefCon);
  1222.     END;
  1223.  
  1224. {------------------------------------------------------------------------------------------------}
  1225.  
  1226. PROCEDURE KTHISAPPApp.InstallScriptHandlers;
  1227.     BEGIN
  1228.         gInHandler := FALSE;
  1229.  
  1230.         {This app is written under TCL, which checks for presence of AppleEvents.}
  1231.         {This method is only called if we know that AppleEvents are kopa setic.}
  1232.  
  1233. {Horrible things happen if we try to run under Symantec Think environment and use scripting, so don't try}
  1234. {$IFC NOT TCL_DEBUG}
  1235.         {create a "null descriptor" to serve as a default container}
  1236.         gNullDesc.descriptorType := typeNull;
  1237.         gNullDesc.dataHandle := NIL;
  1238.  
  1239.         gErrorDesc := gNullDesc;
  1240.  
  1241.         gTempBool := CheckErr(AEInstallEventHandler(kAECoreSuite, kAEGetData, @HandleGetData, 0, FALSE), 1020);
  1242.         gTempBool := CheckErr(AEInstallEventHandler(kAECoreSuite, kAESetData, @HandleSetData, 0, FALSE), 1021);
  1243.         gTempBool := CheckErr(AEInstallEventHandler(kAECoreSuite, kAECountElements, @HandleCountElements, 0, FALSE), 1022);
  1244.  
  1245.         gTempBool := CheckErr(AEObjectInit, 1030);
  1246.         gTempBool := CheckErr(AEInstallObjectAccessor(cProperty, typeNull, @MyObjectAccessor, 0, FALSE), 1031);
  1247.         gTempBool := CheckErr(AEInstallObjectAccessor(typeWildCard, typeWildCard, @MyObjectAccessor, 1, FALSE), 1032);
  1248.  
  1249.         gTempBool := CheckErr(AESetObjectCallbacks(NIL, @CountCallback, NIL, NIL, NIL, NIL, NIL), 1033);
  1250. {$ENDC}
  1251.     END;
  1252.  
  1253.  
  1254. END.